module  alu     (
        //      input   
        op1,    op2,    aluctr,
        //      output
        out,    carry_out       );
//              aluctr  opera.  info.   
//      arith:  0000    add     op1+op2
//              0001    sub     op2-op1
//              0010    xor     op2^op1
//              0011    andn    op1&~op2
//      shift:  0100    rol
//              0101    sll
//              0110    ror
//              0111    srl
//      set:    1100    seq
//              1101    slt
//              1110    sle
//              1111    sco
//      other:  1000    btr
//              1001    slbi
        input   [15:0]  op1,op2;
        input   [3:0]   aluctr;
        output  [15:0]  out;
        output          carry_out;

        wire    [1:0]   sh_mode;
        wire    [15:0]  shifter_out;
        shifter sh0     (
                .In(op1),
                .Cnt(op2[3:0]),
                .Op(aluctr[1:0]),
                .Out(shifter_out)       );
        wire    [15:0]  sum, adder_out;
        wire            Cout;
        wire            zero;
        wire            a = aluctr[3];
        wire            b = aluctr[2];
        wire            c = aluctr[1];
        wire            d = aluctr[0];
        wire            sub;        //      0 for add; 1 for sub;
        assign  sub = ~a&~b&d | a&b&~c | a&b&~d;

        cla16   add0    (
                .A(op1^{16{sub}}),
                .B(op2),
                .Cin(sub),
                .S(sum),
                .Cout(Cout),
                .zero(zero) );

        //      seq = zero
        //      sle     rd = rt-rs
        //              case 1: rs<0, rt>=0, i.e. rs[15]=1, rt[15]=0
        //              case 2: rs>=0,rt>=0, i.e. rt-rs >= 0, rd[15]=0
        //              case 3: rs<0,rt<0, i.e. rt-rs >= 0, rd[15]=0
        wire            sle;
        assign          sle = op1[15]&~op2[15] |
                                ~op1[15]&~op2[15]&~sum[15] |
                                op1[15]&op2[15]&~sum[15];
        
        assign          adder_out = (aluctr == 4'b1100)?{{15{1'b0}},zero}
                                :   (aluctr == 4'b1101)?{{15{1'b0}},sle&~zero}
                                :   (aluctr == 4'b1110)?{{15{1'b0}},sle}
                                :   (aluctr == 4'b1111)?{{15{1'b0}},Cout}:sum;
        
        wire    [15:0]  rev_op1;
        assign  rev_op1 = {
                op1[0],op1[1],op1[2],op1[3],
                op1[4],op1[5],op1[6],op1[7],
                op1[8],op1[9],op1[10],op1[11],
                op1[12],op1[13],op1[14],op1[15]
        };

        assign  out =   (aluctr == 4'b1000) ? rev_op1 
                :   (aluctr == 4'b1001) ? {op1[7:0],op2[7:0]}   //slbi
                :   (aluctr[3:2] == 2'b01) ? shifter_out 
                :   (aluctr == 4'b0010) ? op1^op2
                :   (aluctr == 4'b0011) ? op1&~op2 : adder_out;

endmodule
